/**
 *@File IdleMain.cpp
 *
 *@Author: Bob
 *
 *@Create Time: 2017-02-24 17:39:33
 *
 *@Last Modify: Bob
 *
 *@Last Modify Time: 2017-02-24 17:39:33
 *
 *@Description:
 *   MacOS下键盘和鼠标的监控
 *	 编译时需要在Build Settings->Link Binary With Libraries添加CoreGraphics.framework CoreFoundation.framework两个framework
 */


#include <iostream>
#include <thread>
#include <chrono>
#include <CoreGraphics/CoreGraphics.h>
#include <CoreFoundation/CoreFoundation.h>


static CGRect g_ScreenBounds;
/*
 typedef struct _Record
 {
 bool bIsIdle;
 }Record;
 
 
 CGEventRef EventTapCallBack(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
 {
 //  if (type == kCGEventTapDisabledByTimeout)
 //  {
 //     CGEventTapEnable((CFMachPortRef)refcon, true);
 //     std::cout << "kCGEventTapDisabledByTimeout" << std::endl;
 //}
 
 static int hit = 0;
 if (type == kCGEventLeftMouseDown)
 {
 std::cout<< "Event left mouse down : " << hit++ << std::endl;
 Record *rec = (Record *)refcon;
 rec->bIsIdle = false;
 }
 else if(type == kCGEventKeyDown)
 {
 std::cout << "Event key down: " <<  hit++ << std::endl;
 }
 else
 {
 std::cout << "Event Type: "<< type << std::endl;
 }
 
 return event;
 }
 
 
 CGEventRef MouseMoveCallBack(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
 {
 if(type != kCGEventMouseMoved)
 {
 return event;
 }
 
 // Get the incoming muose position, then show it
 CGPoint point = CGEventGetLocation(event);
 std::cout << "Point:(" <<point.x << ","  << point.y << ")" << std::endl;
 return event;
 }
 
 
 
 bool MonitorKeyboardAndMouse(Record &rec)
 {
 CGEventMask mouseMask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventLeftMouseDown);
 
 // Try to create keyboard-only hook. It will fail if Assistive Devices are not set.
 CFMachPortRef mMachPortRef = CGEventTapCreate(kCGHIDEventTap, //kCGHIDEventTap
 kCGTailAppendEventTap, // kCGHeadInsertEventTap
 kCGEventTapOptionListenOnly, //kCGEventTapOptionDefault
 mouseMask,
 EventTapCallBack,
 (void *)&rec);
 
 if (mMachPortRef != NULL)
 {
 CFRelease(mMachPortRef);
 }
 else
 {
 std::cout << "Can't install mouse hook." << std::endl;
 return false;
 }
 
 CFRunLoopSourceRef EventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mMachPortRef, 0);
 if (EventSrc != NULL)
 {
 CFRelease(EventSrc);
 }
 else
 {
 std::cout << "Can't create runloop." << std::endl;
 return false;
 }
 
 CFRunLoopAddSource(CFRunLoopGetCurrent(), EventSrc, kCFRunLoopCommonModes);
 CGEventTapEnable(mMachPortRef, true);
 CFRunLoopRun();
 
 //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 //CFRunLoopRemoveSource(CFRunLoopGetCurrent(), EventSrc, kCFRunLoopCommonModes);
 return true;
 }
 
 
 bool MonitorMouseMove()
 {
 // The screen size of the primary display
 g_ScreenBounds = CGDisplayBounds(CGMainDisplayID());
 std::cout << "The main screen is: " << g_ScreenBounds.size.width << "*" << g_ScreenBounds.size.height << std::endl;
 
 // Create an event tap, here we are insterested in mouse mobement
 CGEventMask eventMask = (1 << kCGEventMouseMoved);
 CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap,
 kCGHeadInsertEventTap,
 kCGEventTapOptionDefault,
 eventMask,
 MouseMoveCallBack,
 NULL);
 if (eventTap != NULL)
 {
 CFRelease(eventTap);
 }
 else
 {
 std::cout << "CGEventTapCreate failed" << std::endl;
 return false;
 }
 
 // Create a run loop source
 CFRunLoopSourceRef runLoopRef = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
 if (runLoopRef != NULL)
 {
 CFRelease(runLoopRef);
 }
 else
 {
 CFRelease(eventTap);
 return false;
 }
 
 // Add to the current run loop
 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopRef, kCFRunLoopCommonModes);
 
 // Enable the tap
 CGEventTapEnable(eventTap, true);
 
 //Set it all running
 CFRunLoopRun();
 
 return true;
 }
 
 
 
 int main(int argc, const char * argv[])
 {
 Record rec;
 bool bRet = MonitorKeyboardAndMouse(rec);
 if (bRet)
 {
 std::cout << "installKeyboardLogger ok!" << std::endl;
 }
 else
 {
 std::cout << "installKeyboardLogger failed!" << std::endl;
 }
 
 return 0;
 }
 
 */



class Idle
{
public:
    Idle()
    {
        
    }
    
    ~Idle()
    {
        
    }
    
    static Idle GetInstance()
    {
        static Idle idle;
        return idle;
    }
    
    
    static unsigned long long m_llLastTime;
    
    static CGEventRef EventTapCallBack(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
    {
        if(type == kCGEventKeyDown || type == kCGEventLeftMouseDown)
        {
            m_llLastTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            std::cout << "EventTapCallBack now." <<  m_llLastTime << std::endl;
        }
        return event;
    }
    
    static void MonitorKeyboardAndMouse()
    {
        CGEventMask mouseMask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventLeftMouseDown);
        CFMachPortRef mMachPortRef = CGEventTapCreate(kCGHIDEventTap, //kCGHIDEventTap
                                                      kCGTailAppendEventTap, // kCGHeadInsertEventTap
                                                      kCGEventTapOptionListenOnly, //kCGEventTapOptionDefault
                                                      mouseMask,
                                                      EventTapCallBack,
                                                      NULL);
        
        if (mMachPortRef != NULL)
        {
            CFRelease(mMachPortRef);
        }
        else
        {
            std::cout << "Can't install mouse hook." << std::endl;
            return ;
        }
        
        CFRunLoopSourceRef EventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mMachPortRef, 0);
        if (EventSrc != NULL)
        {
            CFRelease(EventSrc);
        }
        else
        {
            std::cout << "Can't create runloop." << std::endl;
            return ;
        }
        
        CFRunLoopAddSource(CFRunLoopGetCurrent(), EventSrc, kCFRunLoopCommonModes);
        CGEventTapEnable(mMachPortRef, true);
        CFRunLoopRun();
    }
    
};

unsigned long long Idle::m_llLastTime =0;


int main(int argc, char* argv[])
{
    //  Idle idle;
    std::thread th(&Idle::MonitorKeyboardAndMouse);
    while (true)
    {
        unsigned long long cur = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        if ((cur - Idle::m_llLastTime) > (1000*60))
        {
            std::cout << "Idle now." <<  cur << " last input time:" << Idle::m_llLastTime << std::endl;
        }
    }
    
    return 0;
}
